// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#include "sw/device/tests/penetrationtests/firmware/lib/pentest_lib.h"

#include "hw/ip/aes/model/aes.h"
#include "sw/device/lib/arch/device.h"
#include "sw/device/lib/base/abs_mmio.h"
#include "sw/device/lib/base/bitfield.h"
#include "sw/device/lib/base/csr.h"
#include "sw/device/lib/base/macros.h"
#include "sw/device/lib/base/mmio.h"
#include "sw/device/lib/base/status.h"
#include "sw/device/lib/dif/dif_alert_handler.h"
#include "sw/device/lib/dif/dif_clkmgr.h"
#include "sw/device/lib/dif/dif_csrng.h"
#include "sw/device/lib/dif/dif_csrng_shared.h"
#include "sw/device/lib/dif/dif_gpio.h"
#include "sw/device/lib/dif/dif_lc_ctrl.h"
#include "sw/device/lib/dif/dif_pinmux.h"
#include "sw/device/lib/dif/dif_rom_ctrl.h"
#include "sw/device/lib/dif/dif_rstmgr.h"
#include "sw/device/lib/dif/dif_rv_core_ibex.h"
#include "sw/device/lib/dif/dif_rv_plic.h"
#include "sw/device/lib/dif/dif_rv_timer.h"
#include "sw/device/lib/dif/dif_sensor_ctrl.h"
#include "sw/device/lib/dif/dif_sram_ctrl.h"
#include "sw/device/lib/dif/dif_uart.h"
#include "sw/device/lib/runtime/hart.h"
#include "sw/device/lib/runtime/irq.h"
#include "sw/device/lib/runtime/print.h"
#include "sw/device/lib/testing/pinmux_testutils.h"
#include "sw/device/lib/testing/rstmgr_testutils.h"
#include "sw/device/lib/testing/rv_plic_testutils.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "sw/device/lib/testing/test_framework/ottf_isrs.h"
#include "sw/device/lib/testing/test_framework/ottf_main.h"
#include "sw/device/lib/testing/test_framework/ujson_ottf.h"
#include "sw/device/lib/ujson/ujson.h"
#include "sw/device/silicon_creator/lib/base/boot_measurements.h"
#include "sw/device/silicon_creator/lib/drivers/flash_ctrl.h"
#include "sw/device/silicon_creator/lib/drivers/retention_sram.h"
#include "sw/device/silicon_creator/lib/ownership/ownership.h"

#include "clkmgr_regs.h"  // Generated
#include "csrng_regs.h"   // Generated
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
#include "rv_core_ibex_regs.h"  // Generated
#include "sensor_ctrl_regs.h"   // Generated.
#include "sram_ctrl_regs.h"     // Generated

#if !OT_IS_ENGLISH_BREAKFAST
#include "sw/device/lib/crypto/drivers/otbn.h"
#include "sw/device/lib/dif/dif_csrng.h"
#include "sw/device/lib/dif/dif_edn.h"
#include "sw/device/lib/dif/dif_entropy_src.h"
#include "sw/device/lib/testing/alert_handler_testutils.h"
#include "sw/device/lib/testing/entropy_testutils.h"

#include "edn_regs.h"  // Generated
#endif

/**
 * Bitfield for the trigger source.
 *
 * Bits 10 and 11 are used to select the trigger source. See chiplevel.sv.tpl
 * for details.
 */
static const bitfield_field32_t kTriggerSourceBitfield = {
    .index = 10,
    .mask = 0x3,
};

enum {
  /**
   * Bit index of the hardware trigger gate signal for gating the hardware
   * trigger from software.
   *
   * See chiplevel.sv.tpl for details.
   */
  kTriggerHwGateBitIndex = 9,
  /**
   * Bit index of the software trigger signal.
   *
   * See chiplevel.sv.tpl for details.
   */
  kTriggerSwBitIndex = 8,
  /**
   * RV timer settings.
   */
  kRvTimerComparator = 0,
  kRvTimerHart = kTopEarlgreyPlicTargetIbex0,
  /**
   * CSR_REG_CPUCTRL[0] is the iCache configuration field.
   */
  kCpuctrlICacheIdx = 0,
  kCpuctrlICacheMask = (1 << kCpuctrlICacheIdx),
  /**
   * CSR_REG_CPUCTRL[1] is the data independent timing field.
   */
  kCpuctrlDataIndTimingIdx = 1,
  kCpuctrlDataIndTimingMask = (1 << kCpuctrlDataIndTimingIdx),
  /**
   * CSR_REG_CPUCTRL[2] is the dummy instruction enable configuration field.
   */
  kCpuctrlDummyInstrEnIdx = 2,
  kCpuctrlDummyInstrEnMask = (1 << kCpuctrlDummyInstrEnIdx),

  /**
   * The dummy instruction count configuration field.
   */
  kCpuctrlDummyInstrCountIdx = 3,
  kCpuctrlDummyInstrCountMask = 7,
};

// By default, we use the precise, hardware-gated capture trigger.
static unsigned int trigger_bit_index = kTriggerHwGateBitIndex;

static dif_gpio_t gpio;
static dif_pinmux_t pinmux;
static dif_rv_timer_t timer;
static dif_sensor_ctrl_t sensor_ctrl;
static dif_uart_t uart0;
static dif_uart_t uart1;

#if !OT_IS_ENGLISH_BREAKFAST
static dif_alert_handler_t alert_handler;
static dif_csrng_t csrng;
static dif_edn_t edn0;
static dif_edn_t edn1;
static dif_lc_ctrl_t lc;
static dif_rv_plic_t plic;
static dif_rstmgr_t rstmgr;
#endif

status_t pentest_configure_entropy_source_max_reseed_interval(void) {
#if !OT_IS_ENGLISH_BREAKFAST
  const dif_csrng_t csrng = {
      .base_addr = mmio_region_from_addr(TOP_EARLGREY_CSRNG_BASE_ADDR)};
  const dif_edn_t edn0 = {
      .base_addr = mmio_region_from_addr(TOP_EARLGREY_EDN0_BASE_ADDR)};
  const dif_edn_t edn1 = {
      .base_addr = mmio_region_from_addr(TOP_EARLGREY_EDN1_BASE_ADDR)};

  TRY(entropy_testutils_stop_all());

  // Re-enable entropy src and csrng.
  TRY(entropy_testutils_entropy_src_init());
  TRY(dif_csrng_configure(&csrng));

  // Re-enable EDN0 in auto mode.
  TRY(dif_edn_set_auto_mode(
      &edn0,
      (dif_edn_auto_params_t){
          // EDN0 provides lower-quality entropy.  Let one generate command
          // return 8
          // blocks, and reseed every 32 generates.
          .instantiate_cmd =
              {
                  .cmd = csrng_cmd_header_build(kCsrngAppCmdInstantiate,
                                                kDifCsrngEntropySrcToggleEnable,
                                                /*cmd_len=*/0,
                                                /*generate_len=*/0),
                  .seed_material =
                      {
                          .len = 0,
                      },
              },
          .reseed_cmd =
              {
                  .cmd = csrng_cmd_header_build(
                      kCsrngAppCmdReseed, kDifCsrngEntropySrcToggleEnable,
                      /*cmd_len=*/0, /*generate_len=*/0),
                  .seed_material =
                      {
                          .len = 0,
                      },
              },
          .generate_cmd =
              {
                  // Generate 8 128-bit blocks.
                  .cmd = csrng_cmd_header_build(kCsrngAppCmdGenerate,
                                                kDifCsrngEntropySrcToggleEnable,
                                                /*cmd_len=*/0,
                                                /*generate_len=*/8),
                  .seed_material =
                      {
                          .len = 0,
                      },
              },
          // Reseed every 0xffffffff generates.
          .reseed_interval = 0xffffffff,
      }));

  // Re-enable EDN1 in auto mode.
  TRY(dif_edn_set_auto_mode(
      &edn1,
      (dif_edn_auto_params_t){
          // EDN1 provides highest-quality entropy.  Let one generate command
          // return 1 block, and reseed after every generate.
          .instantiate_cmd =
              {
                  .cmd = csrng_cmd_header_build(kCsrngAppCmdInstantiate,
                                                kDifCsrngEntropySrcToggleEnable,
                                                /*cmd_len=*/0,
                                                /*generate_len=*/0),
                  .seed_material =
                      {
                          .len = 0,
                      },
              },
          .reseed_cmd =
              {
                  .cmd = csrng_cmd_header_build(
                      kCsrngAppCmdReseed, kDifCsrngEntropySrcToggleEnable,
                      /*cmd_len=*/0, /*generate_len=*/0),
                  .seed_material =
                      {
                          .len = 0,
                      },
              },
          .generate_cmd =
              {
                  // Generate 1 128-bit block.
                  .cmd = csrng_cmd_header_build(kCsrngAppCmdGenerate,
                                                kDifCsrngEntropySrcToggleEnable,
                                                /*cmd_len=*/0,
                                                /*generate_len=*/1),
                  .seed_material =
                      {
                          .len = 0,
                      },
              },
          // Reseed after every 0xffffffff generates.
          .reseed_interval = 0xffffffff,
      }));
#endif
  return OK_STATUS();
}

pentest_sensor_alerts_t pentest_get_sensor_alerts(void) {
  pentest_sensor_alerts_t registered;
  memset(registered.alerts, 0, sizeof(registered.alerts));
  CHECK_DIF_OK(
      dif_sensor_ctrl_get_fatal_events(&sensor_ctrl, &registered.alerts[0]));
  CHECK_DIF_OK(
      dif_sensor_ctrl_get_recov_events(&sensor_ctrl, &registered.alerts[1]));
  return registered;
}

void pentest_clear_sensor_recov_alerts(void) {
  for (size_t it = 0; it < SENSOR_CTRL_PARAM_NUM_ALERT_EVENTS; it++) {
    CHECK_DIF_OK(dif_sensor_ctrl_clear_recov_event(&sensor_ctrl, it));
  }
}

pentest_registered_alerts_t pentest_get_triggered_alerts(void) {
  pentest_registered_alerts_t registered;
  memset(registered.alerts, 0, sizeof(registered.alerts));

#if !OT_IS_ENGLISH_BREAKFAST
  bool is_cause;
  // Loop over all alert_cause regs
  for (size_t alert = 0; alert < ALERT_HANDLER_PARAM_N_ALERTS; alert++) {
    CHECK_DIF_OK(
        dif_alert_handler_alert_is_cause(&alert_handler, alert, &is_cause));
    if (is_cause) {
      if (alert < 32) {
        registered.alerts[0] |= (1 << alert);
      } else if (alert < 64) {
        registered.alerts[1] |= (1 << (alert - 32));
      } else {
        registered.alerts[2] |= (1 << (alert - 64));
      }
    }
  }

  // Loop over all alert_cause regs.
  for (dif_alert_handler_alert_t i = 0; i < ALERT_HANDLER_PARAM_N_ALERTS; i++) {
    CHECK_DIF_OK(dif_alert_handler_alert_acknowledge(&alert_handler, i));
  }
#endif

  return registered;
}

void pentest_configure_alert_handler(
    uint32_t input_alert_classes[ALERT_HANDLER_PARAM_N_ALERTS],
    bool input_alert_en[ALERT_HANDLER_PARAM_N_ALERTS],
    bool input_class_en[ALERT_HANDLER_PARAM_N_CLASSES],
    uint16_t accumulation_thresholds[ALERT_HANDLER_PARAM_N_CLASSES],
    uint32_t signals[ALERT_HANDLER_PARAM_N_CLASSES],
    uint32_t duration_cycles[ALERT_HANDLER_PARAM_N_CLASSES],
    uint32_t ping_timeout) {
#if !OT_IS_ENGLISH_BREAKFAST
  irq_global_ctrl(true);
  irq_external_ctrl(true);

  mmio_region_t base_addr =
      mmio_region_from_addr(TOP_EARLGREY_RV_PLIC_BASE_ADDR);
  CHECK_DIF_OK(dif_rv_plic_init(base_addr, &plic));

  base_addr = mmio_region_from_addr(TOP_EARLGREY_ALERT_HANDLER_BASE_ADDR);
  CHECK_DIF_OK(dif_alert_handler_init(base_addr, &alert_handler));

  // Configure each alert to be enabled or not and assign it to a class.
  for (dif_alert_handler_alert_t i = 0; i < ALERT_HANDLER_PARAM_N_ALERTS; ++i) {
    CHECK_DIF_OK(dif_alert_handler_configure_alert(
        &alert_handler, i, input_alert_classes[i], input_alert_en[i],
        kDifToggleDisabled));
  }

  // Link the escalation phases to the escalation signals and sets the duration
  // for each phase. Recall that escalation signal 0 is NMI, 1 is wipe secrets,
  // 2 is scrap state, 3 is reset (the value of signals[i]).
  dif_alert_handler_escalation_phase_t esc_phases[] = {
      {.phase = kDifAlertHandlerClassStatePhase0,
       .signal = signals[0],
       .duration_cycles = duration_cycles[0]},
      {.phase = kDifAlertHandlerClassStatePhase1,
       .signal = signals[1],
       .duration_cycles = duration_cycles[1]},
      {.phase = kDifAlertHandlerClassStatePhase2,
       .signal = signals[2],
       .duration_cycles = duration_cycles[2]},
      {.phase = kDifAlertHandlerClassStatePhase3,
       .signal = signals[3],
       .duration_cycles = duration_cycles[3]}};

  // Configure each class to leave its accumulation counter unlocked, setting
  // the threshold for how many alerts start the first escalation phase, and
  // which escalation phases it follows.
  for (dif_alert_handler_class_t class = 0;
       class < ALERT_HANDLER_PARAM_N_CLASSES; class ++) {
    dif_alert_handler_class_config_t class_config = {
        .auto_lock_accumulation_counter = kDifToggleDisabled,
        .accumulator_threshold = accumulation_thresholds[class],
        .irq_deadline_cycles = 0,
        .escalation_phases = esc_phases,
        .escalation_phases_len = ARRAYSIZE(esc_phases),
        .crashdump_escalation_phase = kDifAlertHandlerClassStatePhase0,
    };

    CHECK_DIF_OK(dif_alert_handler_configure_class(
        &alert_handler, class, class_config, input_class_en[class],
        kDifToggleDisabled));
  }

  // Configure the ping timeout.
  CHECK_DIF_OK(dif_alert_handler_configure_ping_timer(
      &alert_handler, ping_timeout, kDifToggleEnabled, kDifToggleDisabled));

  // Enables alert handler irq.
  CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(
      &alert_handler, kDifAlertHandlerIrqClassa, kDifToggleEnabled));
#endif
}

status_t pentest_print_config_alert_handler(
    uint32_t alert_classes[ALERT_HANDLER_PARAM_N_ALERTS],
    bool enabled_alerts[ALERT_HANDLER_PARAM_N_ALERTS],
    bool enabled_classes[ALERT_HANDLER_PARAM_N_CLASSES],
    uint32_t accumulation_thresholds[ALERT_HANDLER_PARAM_N_CLASSES],
    uint32_t duration_cycles[ALERT_HANDLER_PARAM_N_PHASES],
    bool escalation_signals_en[ALERT_HANDLER_PARAM_N_PHASES],
    uint32_t escalation_signals_map[ALERT_HANDLER_PARAM_N_PHASES]) {
  for (dif_alert_handler_alert_t alert = 0;
       alert < ALERT_HANDLER_PARAM_N_ALERTS; alert++) {
    // Read the classification of the alert.
    ptrdiff_t class_reg_offset =
        ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_REG_OFFSET +
        (ptrdiff_t)alert * (ptrdiff_t)sizeof(uint32_t);
    alert_classes[alert] =
        mmio_region_read32(alert_handler.base_addr, class_reg_offset);

    // Read the enablement of the alert.
    ptrdiff_t enable_reg_offset =
        ALERT_HANDLER_ALERT_EN_SHADOWED_0_REG_OFFSET +
        (ptrdiff_t)alert * (ptrdiff_t)sizeof(uint32_t);
    enabled_alerts[alert] =
        mmio_region_read32(alert_handler.base_addr, enable_reg_offset);
  }

  // Check whether the alert classes are enabled.
  for (dif_alert_handler_class_t alert_class = 0;
       alert_class < ALERT_HANDLER_PARAM_N_CLASSES; alert_class++) {
    CHECK_DIF_OK(dif_alert_handler_is_class_enabled(
        &alert_handler, alert_class, &enabled_classes[(int)alert_class]));
  }

  // Read the accumulation thresholds for the classes.
  ptrdiff_t accum_thresh_reg_offsetA =
      ALERT_HANDLER_CLASSA_ACCUM_THRESH_SHADOWED_REG_OFFSET;
  accumulation_thresholds[0] =
      mmio_region_read32(alert_handler.base_addr, accum_thresh_reg_offsetA);
  ptrdiff_t accum_thresh_reg_offsetB =
      ALERT_HANDLER_CLASSB_ACCUM_THRESH_SHADOWED_REG_OFFSET;
  accumulation_thresholds[1] =
      mmio_region_read32(alert_handler.base_addr, accum_thresh_reg_offsetB);
  ptrdiff_t accum_thresh_reg_offsetC =
      ALERT_HANDLER_CLASSC_ACCUM_THRESH_SHADOWED_REG_OFFSET;
  accumulation_thresholds[2] =
      mmio_region_read32(alert_handler.base_addr, accum_thresh_reg_offsetC);
  ptrdiff_t accum_thresh_reg_offsetD =
      ALERT_HANDLER_CLASSD_ACCUM_THRESH_SHADOWED_REG_OFFSET;
  accumulation_thresholds[3] =
      mmio_region_read32(alert_handler.base_addr, accum_thresh_reg_offsetD);

  // Read the duration cycles for the phases.
  ptrdiff_t phase0_cycles_reg_offset =
      ALERT_HANDLER_CLASSA_PHASE0_CYC_SHADOWED_REG_OFFSET;
  duration_cycles[0] =
      mmio_region_read32(alert_handler.base_addr, phase0_cycles_reg_offset);
  ptrdiff_t phase1_cycles_reg_offset =
      ALERT_HANDLER_CLASSA_PHASE1_CYC_SHADOWED_REG_OFFSET;
  duration_cycles[1] =
      mmio_region_read32(alert_handler.base_addr, phase1_cycles_reg_offset);
  ptrdiff_t phase2_cycles_reg_offset =
      ALERT_HANDLER_CLASSA_PHASE2_CYC_SHADOWED_REG_OFFSET;
  duration_cycles[2] =
      mmio_region_read32(alert_handler.base_addr, phase2_cycles_reg_offset);
  ptrdiff_t phase3_cycles_reg_offset =
      ALERT_HANDLER_CLASSA_PHASE3_CYC_SHADOWED_REG_OFFSET;
  duration_cycles[3] =
      mmio_region_read32(alert_handler.base_addr, phase3_cycles_reg_offset);

  ptrdiff_t ctrl_reg_offset = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_REG_OFFSET;
  uint32_t ctrl_reg =
      mmio_region_read32(alert_handler.base_addr, ctrl_reg_offset);

  // Read whether each escalation signal is enabled.
  bitfield_bit32_index_t signal_enable_bit_A =
      ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E0_BIT;
  escalation_signals_en[0] = bitfield_bit32_read(ctrl_reg, signal_enable_bit_A);
  bitfield_bit32_index_t signal_enable_bit_B =
      ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E1_BIT;
  escalation_signals_en[1] = bitfield_bit32_read(ctrl_reg, signal_enable_bit_B);
  bitfield_bit32_index_t signal_enable_bit_C =
      ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E2_BIT;
  escalation_signals_en[2] = bitfield_bit32_read(ctrl_reg, signal_enable_bit_C);
  bitfield_bit32_index_t signal_enable_bit_D =
      ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E3_BIT;
  escalation_signals_en[3] = bitfield_bit32_read(ctrl_reg, signal_enable_bit_D);

  // Read how each escalation signal is mapped.
  escalation_signals_map[0] = bitfield_field32_read(
      ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E0_FIELD);
  escalation_signals_map[1] = bitfield_field32_read(
      ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E1_FIELD);
  escalation_signals_map[2] = bitfield_field32_read(
      ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E2_FIELD);
  escalation_signals_map[3] = bitfield_field32_read(
      ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E3_FIELD);

  return OK_STATUS();
}

status_t pentest_send_alert_config(ujson_t *uj) {
  penetrationtest_alert_config_out_t uj_output_alerts;

  TRY(pentest_print_config_alert_handler(
      uj_output_alerts.alert_classes, uj_output_alerts.enabled_alerts,
      uj_output_alerts.enabled_classes,
      uj_output_alerts.accumulation_thresholds,
      uj_output_alerts.duration_cycles, uj_output_alerts.escalation_signals_en,
      uj_output_alerts.escalation_signals_map));

  RESP_OK(ujson_serialize_penetrationtest_alert_config_out_t, uj,
          &uj_output_alerts);

  return OK_STATUS();
}

status_t pentest_print_config_sensor_ctrl(
    bool sensor_ctrl_en[SENSOR_CTRL_PARAM_NUM_ALERT_EVENTS],
    bool sensor_ctrl_fatal[SENSOR_CTRL_PARAM_NUM_ALERT_EVENTS]) {
  // Check whether the sensors are enabled
  for (uint32_t k = 0; k < SENSOR_CTRL_PARAM_NUM_ALERT_EVENTS; k++) {
    sensor_ctrl_en[k] =
        dif_toggle_to_bool(dif_multi_bit_bool_to_toggle(mmio_region_read32(
            sensor_ctrl.base_addr,
            SENSOR_CTRL_ALERT_EN_0_REG_OFFSET + ((ptrdiff_t)k << 2))));
  }

  // Check whether the sensors are configured as recoverable or fatal.
  uint32_t reg = mmio_region_read32(sensor_ctrl.base_addr,
                                    SENSOR_CTRL_FATAL_ALERT_EN_REG_OFFSET);
  for (uint32_t k = 0; k < SENSOR_CTRL_PARAM_NUM_ALERT_EVENTS; k++) {
    sensor_ctrl_fatal[k] = bitfield_bit32_read(reg, k);
  }

  return OK_STATUS();
}

status_t pentest_send_sensor_config(ujson_t *uj) {
  penetrationtest_sensor_config_out_t uj_output_sensors;

  TRY(pentest_print_config_sensor_ctrl(uj_output_sensors.sensor_ctrl_en,
                                       uj_output_sensors.sensor_ctrl_fatal));

  RESP_OK(ujson_serialize_penetrationtest_sensor_config_out_t, uj,
          &uj_output_sensors);

  return OK_STATUS();
}

status_t pentest_read_device_id(uint32_t device_id[]) {
#if !OT_IS_ENGLISH_BREAKFAST
  mmio_region_t lc_reg =
      mmio_region_from_addr(TOP_EARLGREY_LC_CTRL_REGS_BASE_ADDR);
  CHECK_DIF_OK(dif_lc_ctrl_init(lc_reg, &lc));

  dif_lc_ctrl_device_id_t lc_device_id;
  CHECK_DIF_OK(dif_lc_ctrl_get_device_id(&lc, &lc_device_id));
  memcpy(device_id, lc_device_id.data, 8 * sizeof(uint32_t));
#else
  memset(device_id, 0, 8 * sizeof(uint32_t));
#endif

  return OK_STATUS();
}

status_t pentest_read_rom_digest(uint32_t rom_digest[]) {
  static dif_rom_ctrl_t rom_ctrl;
  // initialize rom_ctrl
  mmio_region_t rom_ctrl_reg =
      mmio_region_from_addr(TOP_EARLGREY_ROM_CTRL_REGS_BASE_ADDR);
  TRY(dif_rom_ctrl_init(rom_ctrl_reg, &rom_ctrl));

  dif_rom_ctrl_digest_t expected_digest;
  TRY(dif_rom_ctrl_get_expected_digest(&rom_ctrl, &expected_digest));

  memcpy(rom_digest, expected_digest.digest, 8 * sizeof(uint32_t));

  return OK_STATUS();
}

status_t pentest_configure_cpu(
    bool enable_icache, bool *icache_en, bool enable_dummy_instr,
    bool *dummy_instr_en, uint32_t dummy_instr_count, bool enable_jittery_clock,
    bool enable_sram_readback, bool *clock_jitter_locked, bool *clock_jitter_en,
    bool *sram_main_readback_locked, bool *sram_ret_readback_locked,
    bool *sram_main_readback_en, bool *sram_ret_readback_en,
    bool enable_data_ind_timing, bool *data_ind_timing_en) {
  // Enable/disable the iCache.
  if (enable_icache) {
    // Set CSR_REG_CPUCTRL[0].
    CSR_SET_BITS(CSR_REG_CPUCTRL, kCpuctrlICacheMask);
  } else {
    // Set CSR_REG_CPUCTRL[0].
    CSR_CLEAR_BITS(CSR_REG_CPUCTRL, kCpuctrlICacheMask);
  }
  *icache_en = false;
  // Read back the config stored in CSR_REG_CPUCTRL[0].
  uint32_t csr;
  CSR_READ(CSR_REG_CPUCTRL, &csr);
  if (((csr & kCpuctrlICacheMask) >> kCpuctrlICacheIdx) == 1) {
    *icache_en = true;
  }

  // Enable/disable data independent timing.
  if (enable_data_ind_timing) {
    // Set CSR_REG_CPUCTRL[1].
    CSR_SET_BITS(CSR_REG_CPUCTRL, kCpuctrlDataIndTimingMask);
  } else {
    // Set CSR_REG_CPUCTRL[1].
    CSR_CLEAR_BITS(CSR_REG_CPUCTRL, kCpuctrlDataIndTimingMask);
  }
  *data_ind_timing_en = false;
  // Read back the config stored in CSR_REG_CPUCTRL[1].
  CSR_READ(CSR_REG_CPUCTRL, &csr);
  if (((csr & kCpuctrlDataIndTimingMask) >> kCpuctrlDataIndTimingIdx) == 1) {
    *data_ind_timing_en = true;
  }

  // Enable/disable dummy instructions.
  if (enable_dummy_instr) {
    // Set CSR_REG_CPUCTRL[2].
    CSR_SET_BITS(CSR_REG_CPUCTRL, kCpuctrlDummyInstrEnMask);
    uint32_t cpuctrl_csr;
    CSR_READ(CSR_REG_CPUCTRL, &cpuctrl_csr);
    // 0 Dummy instruction every 0 - 4 real instructions
    // 1 Dummy instruction every 0 - 8 real instructions
    // 3 Dummy instruction every 0 - 16 real instructions
    // 7 Dummy instruction every 0 - 32 real instructions
    cpuctrl_csr = bitfield_field32_write(
        cpuctrl_csr,
        (bitfield_field32_t){.mask = kCpuctrlDummyInstrCountMask,
                             .index = kCpuctrlDummyInstrCountIdx},
        dummy_instr_count);
    CSR_WRITE(CSR_REG_CPUCTRL, cpuctrl_csr);
    uint32_t check_csr;
    CSR_READ(CSR_REG_CPUCTRL, &check_csr);
    if ((((check_csr >> kCpuctrlDummyInstrCountIdx) &
          kCpuctrlDummyInstrCountMask)) == dummy_instr_count) {
      LOG_INFO("CPUCTRL register correctly set");
    } else {
      LOG_INFO("Something in CPUCTRL was locked");
    }
  } else {
    // Clear CSR_REG_CPUCTRL[2].
    CSR_CLEAR_BITS(CSR_REG_CPUCTRL, kCpuctrlDummyInstrEnMask);
  }
  *dummy_instr_en = false;
  // Read back the config stored in CSR_REG_CPUCTRL[2].
  CSR_READ(CSR_REG_CPUCTRL, &csr);
  if (((csr & kCpuctrlDummyInstrEnMask) >> kCpuctrlDummyInstrEnIdx) == 1) {
    *dummy_instr_en = true;
  }

  // Enable or disable the jittery clock.
  dif_clkmgr_t clkmgr;
  TRY(dif_clkmgr_init(mmio_region_from_addr(TOP_EARLGREY_CLKMGR_AON_BASE_ADDR),
                      &clkmgr));

  TRY(dif_clkmgr_jitter_enable_is_locked(&clkmgr, clock_jitter_locked));
  dif_toggle_t clock_jitter_state;
  TRY(dif_clkmgr_jitter_get_enabled(&clkmgr, &clock_jitter_state));

  if (!*clock_jitter_locked) {
    if (enable_jittery_clock) {
      TRY(dif_clkmgr_jitter_set_enabled(&clkmgr, kDifToggleEnabled));
    } else {
      mmio_region_write32(clkmgr.base_addr, CLKMGR_JITTER_ENABLE_REG_OFFSET,
                          kMultiBitBool4False);
    }
    TRY(dif_clkmgr_jitter_get_enabled(&clkmgr, &clock_jitter_state));
  }

  *clock_jitter_en = (clock_jitter_state == kDifToggleDisabled) ? false : true;

  // Enable or disable SRAM main and ret readback feature.
  dif_sram_ctrl_t sram_ctrl_ret;
  dif_sram_ctrl_t sram_ctrl_main;
  TRY(dif_sram_ctrl_init(
      mmio_region_from_addr(TOP_EARLGREY_SRAM_CTRL_RET_AON_REGS_BASE_ADDR),
      &sram_ctrl_ret));
  TRY(dif_sram_ctrl_init(
      mmio_region_from_addr(TOP_EARLGREY_SRAM_CTRL_MAIN_REGS_BASE_ADDR),
      &sram_ctrl_main));

  dif_result_t ret_locked;
  dif_result_t main_locked;
  if (enable_sram_readback) {
    ret_locked = dif_sram_ctrl_readback_set(&sram_ctrl_ret, kDifToggleEnabled);
    main_locked =
        dif_sram_ctrl_readback_set(&sram_ctrl_main, kDifToggleEnabled);
  } else {
    ret_locked = dif_sram_ctrl_readback_set(&sram_ctrl_ret, kDifToggleDisabled);
    main_locked =
        dif_sram_ctrl_readback_set(&sram_ctrl_main, kDifToggleDisabled);
  }

  uint32_t ret_status =
      abs_mmio_read32(TOP_EARLGREY_SRAM_CTRL_RET_AON_REGS_BASE_ADDR +
                      SRAM_CTRL_READBACK_REG_OFFSET);
  uint32_t main_status =
      abs_mmio_read32(TOP_EARLGREY_SRAM_CTRL_MAIN_REGS_BASE_ADDR +
                      SRAM_CTRL_READBACK_REG_OFFSET);

  // Report the configuration.
  *sram_main_readback_locked = (main_locked == kDifLocked) ? true : false;
  *sram_ret_readback_locked = (ret_locked == kDifLocked) ? true : false;

  *sram_ret_readback_en = (ret_status == kMultiBitBool4True) ? true : false;
  *sram_main_readback_en = (main_status == kMultiBitBool4True) ? true : false;

  return OK_STATUS();
}

/**
 * Initializes the UART peripheral.
 */
static void pentest_init_uart(void) {
  CHECK(kUartBaudrate <= UINT32_MAX, "kUartBaudrate must fit in uint32_t");
  CHECK(kClockFreqPeripheralHz <= UINT32_MAX,
        "kClockFreqPeripheralHz must fit in uint32_t");
  const dif_uart_config_t uart_config = {
      .baudrate = (uint32_t)kUartBaudrate,
      .clk_freq_hz = (uint32_t)kClockFreqPeripheralHz,
      .parity_enable = kDifToggleDisabled,
      .parity = kDifUartParityEven,
      .tx_enable = kDifToggleEnabled,
      .rx_enable = kDifToggleEnabled,
  };

  OT_DISCARD(dif_uart_init(mmio_region_from_addr(TOP_EARLGREY_UART0_BASE_ADDR),
                           &uart0));
  OT_DISCARD(dif_uart_configure(&uart0, uart_config));
  base_uart_stdout(&uart0);

#if !OT_IS_ENGLISH_BREAKFAST
  OT_DISCARD(dif_uart_init(mmio_region_from_addr(TOP_EARLGREY_UART1_BASE_ADDR),
                           &uart1));
  OT_DISCARD(dif_uart_configure(&uart1, uart_config));
#endif
}

/**
 * Initializes the GPIO peripheral.
 *
 * @param trigger Trigger source.
 */
static void pentest_init_gpio(pentest_trigger_source_t trigger) {
  OT_DISCARD(
      dif_gpio_init(mmio_region_from_addr(TOP_EARLGREY_GPIO_BASE_ADDR), &gpio));

  uint32_t select_mask =
      bitfield_field32_write(0, kTriggerSourceBitfield, UINT32_MAX);
  uint32_t enable_mask = bitfield_bit32_write(0, kTriggerHwGateBitIndex, true);
  enable_mask = bitfield_bit32_write(enable_mask, kTriggerSwBitIndex, true);

  // Configure the pinmux to enable the GPIOs.
  for (size_t i = 0; i < 32; ++i) {
    if ((select_mask | enable_mask) & (1u << i)) {
      dif_pinmux_index_t mio = kTopEarlgreyPinmuxInselIoa0 + i;
      dif_pinmux_index_t periph_io =
          kTopEarlgreyPinmuxPeripheralInGpioGpio0 + i;
      OT_DISCARD(dif_pinmux_input_select(&pinmux, periph_io, mio));

      mio = kTopEarlgreyPinmuxMioOutIoa0 + i;
      periph_io = kTopEarlgreyPinmuxOutselGpioGpio0 + i;
      OT_DISCARD(dif_pinmux_output_select(&pinmux, mio, periph_io));
    }
  }

  OT_DISCARD(dif_gpio_output_set_enabled_all(&gpio, select_mask | enable_mask));

  OT_DISCARD(dif_gpio_write_masked(
      &gpio, select_mask,
      bitfield_field32_write(0, kTriggerSourceBitfield, trigger)));
}

/**
 * Initializes the timer peripheral.
 */
static void pentest_init_timer(void) {
  OT_DISCARD(dif_rv_timer_init(
      mmio_region_from_addr(TOP_EARLGREY_RV_TIMER_BASE_ADDR), &timer));
  OT_DISCARD(dif_rv_timer_reset(&timer));
  dif_rv_timer_tick_params_t tick_params;
  OT_DISCARD(dif_rv_timer_approximate_tick_params(
      kClockFreqPeripheralHz, kClockFreqCpuHz, &tick_params));
  OT_DISCARD(dif_rv_timer_set_tick_params(&timer, kRvTimerHart, tick_params));
  OT_DISCARD(dif_rv_timer_irq_set_enabled(
      &timer, kDifRvTimerIrqTimerExpiredHart0Timer0, kDifToggleEnabled));
  irq_timer_ctrl(true);
  irq_global_ctrl(true);
}

/**
 * Initializes the CSRNG handle.
 */
static void pentest_init_csrng(void) {
#if !OT_IS_ENGLISH_BREAKFAST
  OT_DISCARD(dif_csrng_init(mmio_region_from_addr(TOP_EARLGREY_CSRNG_BASE_ADDR),
                            &csrng));
  OT_DISCARD(dif_csrng_init(mmio_region_from_addr(TOP_EARLGREY_CSRNG_BASE_ADDR),
                            &csrng));
#endif
}

static void pentest_init_sensor_ctrl(void) {
#if !OT_IS_ENGLISH_BREAKFAST
  CHECK_DIF_OK(dif_sensor_ctrl_init(
      mmio_region_from_addr(TOP_EARLGREY_SENSOR_CTRL_AON_BASE_ADDR),
      &sensor_ctrl));
#endif
}

/**
 * Initializes the EDN handle.
 */
static void pentest_init_edn(void) {
#if !OT_IS_ENGLISH_BREAKFAST
  OT_DISCARD(
      dif_edn_init(mmio_region_from_addr(TOP_EARLGREY_EDN0_BASE_ADDR), &edn0));

  OT_DISCARD(
      dif_edn_init(mmio_region_from_addr(TOP_EARLGREY_EDN1_BASE_ADDR), &edn1));
#endif
}

/**
 * Override default Timer ISR.
 *
 * Disables the counter and clears pending interrupts.
 */
void ottf_timer_isr(uint32_t *exc_info) {
  // Return values of below functions are ignored to improve capture
  // performance.
  OT_DISCARD(dif_rv_timer_counter_set_enabled(&timer, kRvTimerHart,
                                              kDifToggleDisabled));
  OT_DISCARD(dif_rv_timer_irq_acknowledge(
      &timer, kDifRvTimerIrqTimerExpiredHart0Timer0));
}

/**
 * Disables the given peripherals to reduce noise during SCA.
 *
 * Care must be taken when disabling the entropy complex if a peripheral that
 * depends on it will be used in SCA. E.g., We can disable the entropy complex
 * when analyzing AES only because AES features a parameter to skip PRNG
 * reseeding for SCA experiments. Without this parameter, AES would simply get
 * stalled with a disabled entropy complex.
 *
 * @param disable Set of peripherals to disable.
 */
void sca_disable_peripherals(pentest_peripherals_t disable) {
#if !OT_IS_ENGLISH_BREAKFAST
  if (disable & kPentestPeripheralEdn) {
    OT_DISCARD(dif_edn_stop(&edn0));
    OT_DISCARD(dif_edn_stop(&edn1));
  }
  if (disable & kPentestPeripheralCsrng) {
    OT_DISCARD(dif_csrng_stop(&csrng));
  }
  if (disable & kPentestPeripheralEntropy) {
    dif_entropy_src_t entropy;
    OT_DISCARD(dif_entropy_src_init(
        mmio_region_from_addr(TOP_EARLGREY_ENTROPY_SRC_BASE_ADDR), &entropy));
    OT_DISCARD(dif_entropy_src_set_enabled(&entropy, kDifToggleDisabled));
  }
#endif

  // Disable HMAC, KMAC, OTBN and USB clocks through CLKMGR DIF.
  dif_clkmgr_t clkmgr;
  OT_DISCARD(dif_clkmgr_init(
      mmio_region_from_addr(TOP_EARLGREY_CLKMGR_AON_BASE_ADDR), &clkmgr));

  if (disable & kPentestPeripheralAes) {
    OT_DISCARD(dif_clkmgr_hintable_clock_set_hint(
        &clkmgr, CLKMGR_CLK_HINTS_CLK_MAIN_AES_HINT_BIT, kDifToggleDisabled));
  }
#if !OT_IS_ENGLISH_BREAKFAST
  if (disable & kPentestPeripheralHmac) {
    OT_DISCARD(dif_clkmgr_hintable_clock_set_hint(
        &clkmgr, CLKMGR_CLK_HINTS_CLK_MAIN_HMAC_HINT_BIT, kDifToggleDisabled));
  }
#endif
  if (disable & kPentestPeripheralIoDiv4) {
    OT_DISCARD(dif_clkmgr_gateable_clock_set_enabled(
        &clkmgr, CLKMGR_CLK_ENABLES_CLK_IO_DIV4_PERI_EN_BIT,
        kDifToggleDisabled));
  }
  if (disable & kPentestPeripheralIoDiv2) {
    OT_DISCARD(dif_clkmgr_gateable_clock_set_enabled(
        &clkmgr, CLKMGR_CLK_ENABLES_CLK_IO_DIV2_PERI_EN_BIT,
        kDifToggleDisabled));
  }
  if (disable & kPentestPeripheralUsb) {
    OT_DISCARD(dif_clkmgr_gateable_clock_set_enabled(
        &clkmgr, CLKMGR_CLK_ENABLES_CLK_USB_PERI_EN_BIT, kDifToggleDisabled));
  }
  if (disable & kPentestPeripheralIo) {
    OT_DISCARD(dif_clkmgr_gateable_clock_set_enabled(
        &clkmgr, CLKMGR_CLK_ENABLES_CLK_IO_PERI_EN_BIT, kDifToggleDisabled));
  }

#if !OT_IS_ENGLISH_BREAKFAST
  if (disable & kPentestPeripheralKmac) {
    OT_DISCARD(dif_clkmgr_hintable_clock_set_hint(
        &clkmgr, CLKMGR_CLK_HINTS_CLK_MAIN_KMAC_HINT_BIT, kDifToggleDisabled));
  }
  if (disable & kPentestPeripheralOtbn) {
    OT_DISCARD(dif_clkmgr_hintable_clock_set_hint(
        &clkmgr, CLKMGR_CLK_HINTS_CLK_MAIN_OTBN_HINT_BIT, kDifToggleDisabled));
  }
#endif
}

void pentest_init(
    pentest_trigger_source_t trigger, pentest_peripherals_t peripheral_enable,
    bool sensor_ctrl_enable,
    bool sensor_ctrl_en_fatal[SENSOR_CTRL_PARAM_NUM_ALERT_EVENTS]) {
  OT_DISCARD(dif_pinmux_init(
      mmio_region_from_addr(TOP_EARLGREY_PINMUX_AON_BASE_ADDR), &pinmux));
  pinmux_testutils_init(&pinmux);
  pentest_init_uart();
  pentest_init_gpio(trigger);
  pentest_init_timer();
  pentest_init_csrng();
  pentest_init_edn();
  pentest_init_sensor_ctrl();
  sca_disable_peripherals(~peripheral_enable);

  // Configure reset manager and enable alert crash dump capture.
  OT_DISCARD(dif_rstmgr_init(
      mmio_region_from_addr(TOP_EARLGREY_RSTMGR_AON_BASE_ADDR), &rstmgr));
  CHECK_DIF_OK(dif_rstmgr_alert_info_set_enabled(&rstmgr, kDifToggleEnabled));

  // Enable all sensor control AST alerts.
  for (uint32_t k = 0; k < SENSOR_CTRL_PARAM_NUM_ALERT_EVENTS; k++) {
    dif_result_t res = dif_sensor_ctrl_set_alert_en(
        &sensor_ctrl, k, dif_bool_to_toggle(sensor_ctrl_enable));
    if (res != kDifOk) {
      LOG_INFO("Enabling/disabling alert %d in sensor control did not work", k);
    }
  }

  // Make alerts fatal or recoverable.
  for (uint32_t k = 0; k < SENSOR_CTRL_PARAM_NUM_ALERT_EVENTS; k++) {
    dif_result_t res = dif_sensor_ctrl_set_alert_fatal(
        &sensor_ctrl, k, dif_bool_to_toggle(sensor_ctrl_en_fatal[k]));
    if (res != kDifOk) {
      LOG_INFO("Setting fatality of alert %d in sensor control did not work",
               k);
    }
  }
}

const dif_uart_t *pentest_get_uart(void) { return &uart1; }

void pentest_select_trigger_type(pentest_trigger_type_t trigger_type) {
  if (trigger_type == kPentestTriggerTypeHwGated) {
    trigger_bit_index = kTriggerHwGateBitIndex;
  } else if (trigger_type == kPentestTriggerTypeSw) {
    trigger_bit_index = kTriggerSwBitIndex;
  }
}

void pentest_set_trigger_high(void) {
  OT_DISCARD(dif_gpio_write(&gpio, trigger_bit_index, true));
}

void pentest_set_trigger_low(void) {
  OT_DISCARD(dif_gpio_write(&gpio, trigger_bit_index, false));
}

void pentest_call_and_sleep(sca_callee callee, uint32_t sleep_cycles,
                            bool sw_trigger, bool otbn) {
  // Disable the IO_DIV4_PERI clock to reduce noise during the actual capture.
  // This also disables the UART(s) and GPIO modules required for
  // communication with the scope. Therefore, it has to be re-enabled after
  // the capture.
  dif_clkmgr_t clkmgr;
  OT_DISCARD(dif_clkmgr_init(
      mmio_region_from_addr(TOP_EARLGREY_CLKMGR_AON_BASE_ADDR), &clkmgr));

  // Start timer to wake Ibex after the callee is done.
  uint64_t current_time;
  // Return values of below functions are ignored to improve capture
  // performance.
  OT_DISCARD(dif_rv_timer_counter_read(&timer, kRvTimerHart, &current_time));
  OT_DISCARD(dif_rv_timer_arm(&timer, kRvTimerHart, kRvTimerComparator,
                              current_time + sleep_cycles));
  OT_DISCARD(dif_rv_timer_counter_set_enabled(&timer, kRvTimerHart,
                                              kDifToggleEnabled));

  if (sw_trigger) {
    pentest_set_trigger_high();
  }

  callee();

  wait_for_interrupt();

#if !OT_IS_ENGLISH_BREAKFAST
  if (otbn) {
    otbn_busy_wait_for_done();
  }
#endif

  if (sw_trigger) {
    pentest_set_trigger_low();
  }

  // Re-enable IO_DIV4_PERI clock to resume communication with the scope.
  OT_DISCARD(dif_clkmgr_gateable_clock_set_enabled(
      &clkmgr, CLKMGR_CLK_ENABLES_CLK_IO_DIV4_PERI_EN_BIT, kDifToggleEnabled));
}

static uint32_t sca_lfsr_state_masking = 0xdeadbeef;
static uint32_t sca_lfsr_state_order = 0x99999999;

void pentest_seed_lfsr(uint32_t seed, pentest_lfsr_context_t context) {
  if (context == kPentestLfsrMasking) {
    sca_lfsr_state_masking = seed;
  }
  if (context == kPentestLfsrOrder) {
    sca_lfsr_state_order = seed;
  }
}

uint32_t pentest_next_lfsr(uint16_t num_steps, pentest_lfsr_context_t context) {
  uint32_t sca_lfsr_state;
  if (context == kPentestLfsrMasking) {
    sca_lfsr_state = sca_lfsr_state_masking;
  }
  if (context == kPentestLfsrOrder) {
    sca_lfsr_state = sca_lfsr_state_order;
  }
  const uint32_t lfsr_out = sca_lfsr_state;
  for (size_t i = 0; i < num_steps; ++i) {
    bool lfsr_bit = sca_lfsr_state & 0x00000001;
    sca_lfsr_state = sca_lfsr_state >> 1;
    if (lfsr_bit) {
      sca_lfsr_state ^= 0x80000057;
    }
  }
  if (context == kPentestLfsrMasking) {
    sca_lfsr_state_masking = sca_lfsr_state;
  }
  if (context == kPentestLfsrOrder) {
    sca_lfsr_state_order = sca_lfsr_state;
  }
  return lfsr_out;
}

uint32_t pentest_linear_layer(uint32_t input) {
  uint32_t output =
      // output[7:0]
      (((input >> 0) & 0x1) << 7) | (((input >> 6) & 0x1) << 6) |
      (((input >> 11) & 0x1) << 5) | (((input >> 14) & 0x1) << 4) |
      (((input >> 1) & 0x1) << 3) | (((input >> 7) & 0x1) << 2) |
      (((input >> 10) & 0x1) << 1) | (((input >> 13) & 0x1) << 0) |
      // output[15:8]
      (((input >> 2) & 0x1) << 15) | (((input >> 4) & 0x1) << 14) |
      (((input >> 9) & 0x1) << 13) | (((input >> 12) & 0x1) << 12) |
      (((input >> 3) & 0x1) << 11) | (((input >> 5) & 0x1) << 10) |
      (((input >> 8) & 0x1) << 9) | (((input >> 15) & 0x1) << 8) |
      // output[23:16]
      (((input >> 16) & 0x1) << 23) | (((input >> 22) & 0x1) << 22) |
      (((input >> 27) & 0x1) << 21) | (((input >> 30) & 0x1) << 20) |
      (((input >> 17) & 0x1) << 19) | (((input >> 23) & 0x1) << 18) |
      (((input >> 26) & 0x1) << 17) | (((input >> 29) & 0x1) << 16) |
      // output[31:24]
      (((input >> 18) & 0x1) << 31) | (((input >> 20) & 0x1) << 30) |
      (((input >> 25) & 0x1) << 29) | (((input >> 28) & 0x1) << 28) |
      (((input >> 19) & 0x1) << 27) | (((input >> 21) & 0x1) << 26) |
      (((input >> 24) & 0x1) << 25) | (((input >> 31) & 0x1) << 24);

  return output;
}

uint32_t pentest_non_linear_layer(uint32_t input) {
  uint32_t output;
  if (input != 0) {
    // Perform the AES S-Box look ups bytewise.
    output = (uint32_t)(sbox[(input >> 24) & 0xFF] << 24) |
             (uint32_t)(sbox[(input >> 16) & 0xFF] << 16) |
             (uint32_t)(sbox[(input >> 8) & 0xFF] << 8) | sbox[input & 0xFF];
  } else {
    output = input;
  }

  return output;
}

status_t pentest_read_rstmgr_alert_info(ujson_t *uj) {
  // Init the reset manager.
  TRY(dif_rstmgr_init(mmio_region_from_addr(TOP_EARLGREY_RSTMGR_AON_BASE_ADDR),
                      &rstmgr));

  // Read the reset reason from the reset manager.
  dif_rstmgr_reset_info_bitfield_t rst_info;
  rst_info = rstmgr_testutils_reason_get();
  rstmgr_testutils_reason_clear();

  // Read the alert dump information.
  dif_rstmgr_alert_info_dump_segment_t dump[DIF_RSTMGR_ALERT_INFO_MAX_SIZE];
  size_t seg_size;
  alert_handler_testutils_info_t actual_info;
  TRY(dif_rstmgr_alert_info_dump_read(
      &rstmgr, dump, DIF_RSTMGR_ALERT_INFO_MAX_SIZE, &seg_size));
  TRY(alert_handler_testutils_info_parse(dump, (int)seg_size, &actual_info));

  // Transmit to host.
  penetrationtest_alert_dump_t uj_output;
  uj_output.rst_reason = rst_info;

  // Clear the array.
  memset(uj_output.alert_cause, 0, sizeof(uj_output.alert_cause));
  memset(uj_output.class_accum_cnt, 0, sizeof(uj_output.class_accum_cnt));
  memset(uj_output.class_esc_cnt, 0, sizeof(uj_output.class_esc_cnt));
  memset(uj_output.class_esc_state, 0, sizeof(uj_output.class_esc_state));

  // Copy the crash dump.
  memcpy(uj_output.alert_cause, actual_info.alert_cause,
         sizeof(uj_output.alert_cause));
  uj_output.loc_alert_cause = actual_info.loc_alert_cause;
  memcpy(uj_output.class_accum_cnt, actual_info.class_accum_cnt,
         sizeof(uj_output.class_accum_cnt));
  memcpy(uj_output.class_esc_cnt, actual_info.class_esc_cnt,
         sizeof(uj_output.class_esc_cnt));
  memcpy(uj_output.class_esc_state, actual_info.class_esc_state,
         sizeof(uj_output.class_esc_state));

  return RESP_OK(ujson_serialize_penetrationtest_alert_dump_t, uj, &uj_output);
}

/**
 * Read out the owner's flash page.
 */
static status_t pentest_get_owner_flash(uint32_t *config_version,
                                        uint32_t *sram_exec_mode,
                                        uint32_t *ownership_key_alg,
                                        uint32_t *update_mode,
                                        uint32_t *min_security_version_bl0,
                                        uint32_t *lock_constraint) {
  TRY(flash_ctrl_info_read(&kFlashCtrlInfoPageOwnerSlot0, 0,
                           sizeof(owner_page[0]) / sizeof(uint32_t),
                           &owner_page[0]));
  // Configuration version (monotonically increasing per owner).
  *config_version = owner_page[0].config_version;
  // SRAM execution configuration (DisabledLocked, Disabled, Enabled).
  *sram_exec_mode = owner_page[0].sram_exec_mode;
  // Ownership key algorithm (currently, only ECDSA is supported).
  *ownership_key_alg = owner_page[0].ownership_key_alg;
  // Ownership update mode (one of OPEN, SELF, NEWV).
  *update_mode = owner_page[0].update_mode;
  // Set the minimum security version to this value (UINT32_MAX: no change).
  *min_security_version_bl0 = owner_page[0].min_security_version_bl0;
  // The device ID locking constraint.
  *lock_constraint = owner_page[0].lock_constraint;
  return OK_STATUS();
}

/**
 * Return the boot_log data from retention RAM.
 */
static void pentest_get_boot_log(
    uint32_t digest[], uint32_t *identifier, uint32_t *scm_revision_low,
    uint32_t *scm_revision_high, uint32_t *rom_ext_slot,
    uint32_t *rom_ext_major, uint32_t *rom_ext_minor, uint32_t *rom_ext_size,
    uint32_t *bl0_slot, uint32_t *ownership_state,
    uint32_t *ownership_transfers, uint32_t *rom_ext_min_sec_ver,
    uint32_t *bl0_min_sec_ver, uint32_t *primary_bl0_slot,
    uint32_t *retention_ram_initialized) {
  boot_log_t boot_log = retention_sram_get()->creator.boot_log;
  // Digest to indicate validity of the boot_log.
  memcpy(digest, boot_log.digest.digest, 8 * sizeof(uint32_t));
  // Identifier (`BLOG`).
  *identifier = boot_log.identifier;
  // Chip version (from the ROM).
  *scm_revision_low = boot_log.chip_version.scm_revision_low;
  *scm_revision_high = boot_log.chip_version.scm_revision_high;
  // Which ROM_EXT slot booted (boot_slot_t).
  *rom_ext_slot = boot_log.rom_ext_slot;
  // ROM_EXT major version number.
  *rom_ext_major = boot_log.rom_ext_major;
  // ROM_EXT minor version number.
  *rom_ext_minor = boot_log.rom_ext_minor;
  // ROM_EXT size in flash.
  *rom_ext_size = boot_log.rom_ext_size;
  // Which BL0 slot booted (boot_slot_t).
  *bl0_slot = boot_log.bl0_slot;
  // Chip ownership state.
  *ownership_state = boot_log.ownership_state;
  // Number of ownership transfers this chip has had.
  *ownership_transfers = boot_log.ownership_transfers;
  // Minimum security version permitted for ROM_EXT payloads.
  *rom_ext_min_sec_ver = boot_log.rom_ext_min_sec_ver;
  // Minimum security version permitted for application payloads.
  *bl0_min_sec_ver = boot_log.bl0_min_sec_ver;
  // Primary BL0 slot.
  *primary_bl0_slot = boot_log.primary_bl0_slot;
  // Whether the RET-RAM was initialized on this boot (hardened_bool_t).
  *retention_ram_initialized = boot_log.retention_ram_initialized;
}

/**
 * Return the boot measurements shared between ROM and ROM_EXT boot stages.
 */
static void pentest_get_boot_measurements(uint32_t bl0[], uint32_t rom_ext[]) {
  // Digest of the BL0
  memcpy(bl0, boot_measurements.bl0.data, 8 * sizeof(uint32_t));
  // Digest of the ROM_EXT
  memcpy(rom_ext, boot_measurements.rom_ext.data, 8 * sizeof(uint32_t));
}

status_t pentest_send_sku_config(ujson_t *uj) {
  // Read the owner flash page and return to host.
  penetrationtest_owner_page_t uj_output_flash;
  pentest_get_owner_flash(
      &uj_output_flash.config_version, &uj_output_flash.sram_exec_mode,
      &uj_output_flash.ownership_key_alg, &uj_output_flash.update_mode,
      &uj_output_flash.min_security_version_bl0,
      &uj_output_flash.lock_constraint);
  RESP_OK(ujson_serialize_penetrationtest_owner_page_t, uj, &uj_output_flash);

  // Read the boot log and return to host.
  penetrationtest_boot_log_t uj_output_boot_log;
  pentest_get_boot_log(
      uj_output_boot_log.digest, &uj_output_boot_log.identifier,
      &uj_output_boot_log.scm_revision_low,
      &uj_output_boot_log.scm_revision_high, &uj_output_boot_log.rom_ext_slot,
      &uj_output_boot_log.rom_ext_major, &uj_output_boot_log.rom_ext_minor,
      &uj_output_boot_log.rom_ext_size, &uj_output_boot_log.bl0_slot,
      &uj_output_boot_log.ownership_state,
      &uj_output_boot_log.ownership_transfers,
      &uj_output_boot_log.rom_ext_min_sec_ver,
      &uj_output_boot_log.bl0_min_sec_ver, &uj_output_boot_log.primary_bl0_slot,
      &uj_output_boot_log.retention_ram_initialized);
  RESP_OK(ujson_serialize_penetrationtest_boot_log_t, uj, &uj_output_boot_log);

  // Read the boot measurements and return to host.
  penetrationtest_boot_measurements_t uj_output_boot_measurements;
  pentest_get_boot_measurements(uj_output_boot_measurements.bl0,
                                uj_output_boot_measurements.rom_ext);
  RESP_OK(ujson_serialize_penetrationtest_boot_measurements_t, uj,
          &uj_output_boot_measurements);

  // Send back string containing the pentest framework version.
  RESP_OK(ujson_serialize_string, uj, PENTEST_VERSION);

  return OK_STATUS();
}

static void wait_rnd_valid(void) {
  while (true) {
    uint32_t reg = abs_mmio_read32(TOP_EARLGREY_RV_CORE_IBEX_CFG_BASE_ADDR +
                                   RV_CORE_IBEX_RND_STATUS_REG_OFFSET);
    if (bitfield_bit32_read(reg, RV_CORE_IBEX_RND_STATUS_RND_DATA_VALID_BIT)) {
      return;
    }
  }
}

uint32_t pentest_ibex_rnd32_read(void) {
  wait_rnd_valid();
  return abs_mmio_read32(TOP_EARLGREY_RV_CORE_IBEX_CFG_BASE_ADDR +
                         RV_CORE_IBEX_RND_DATA_REG_OFFSET);
}
